package net.javastone.toolkit.core.dataformat;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.type.ArrayType;
import com.fasterxml.jackson.databind.type.TypeFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 数据格式转换类.
 */
public class DataFormatTransfer {

  private ObjectMapper objectMapper;
  private Object object;

  public DataFormatTransfer(Object object, ObjectMapper objectMapper) {
    this.object = object;
    this.objectMapper = objectMapper;
  }

  @Override
  public String toString() {
    try {
      return objectMapper.writer().writeValueAsString(object);
    } catch (IOException e) {
      e.printStackTrace();
    }
    return null;
  }

  /**
   * 将对象序列化为字节数组格式.
   *
   * @return 序列化后的字节数组
   * @throws IOException JSON序列化异常
   */
  public byte[] toBytes() throws IOException {
    return objectMapper.writer().writeValueAsBytes(object);
  }

  /**
   * 将对象序列化并存储到文件中
   *
   * @param file 目标文件
   * @return itself
   * @throws IOException 文件操作异常
   */
  public DataFormatTransfer to(File file) throws IOException {
    objectMapper.writer().writeValue(file, object);
    return this;
  }

  /**
   * 将对象序列化并输出到指定的OutputStream中
   *
   * @param outputStream 输出目标 OutputStream
   * @return itseft
   * @throws IOException OutputStream 输出异常
   */
  public DataFormatTransfer to(OutputStream outputStream) throws IOException {
    objectMapper.writer().writeValue(outputStream, object);
    return this;
  }

  /**
   * 将对象序列化并输出到指定的Writer中
   *
   * @param writer 输出目标 Writer
   * @return itseft
   * @throws IOException Writer 输出异常
   */
  public DataFormatTransfer to(Writer writer) throws IOException {
    objectMapper.writer().writeValue(writer, object);
    return this;
  }

  /**
   * 将JSON数据解析并映射给指定的类实例
   *
   * @param clazz 目标对象类型
   * @return 指定类型的实例对象
   * @throws IOException JSON数据解析异常
   */
  public <T> T to(Class<T> clazz) throws IOException {
    return read(objectMapper.readerFor(clazz));
  }

  /**
   * 将JSON数据解析并映射给指定的类实例
   *
   * @param type 目标对象类型
   * @return 指定类型的实例对象
   * @throws IOException JSON数据解析异常
   */
  public <T> T to(JavaType type) throws IOException {
    return read(objectMapper.readerFor(type));
  }

  /**
   * 将JSON数据解析并存一个Map对象中
   *
   * @param keyClass   Map对象键类型
   * @param valueClass Map对象值类型
   * @return 存储JSON数据的Map对象
   * @throws IOException JSON数据解析异常
   */
  public <K, V> Map<K, V> toMap(Class<K> keyClass, Class<V> valueClass) throws IOException {
    JavaType type = objectMapper.getTypeFactory()
        .constructMapType(HashMap.class, keyClass, valueClass);
    return read(objectMapper.readerFor(type));
  }

  /**
   * 将JSON数据解析并存一个Map对象中
   *
   * @return 存储JSON数据的Map对象
   * @throws IOException JSON数据解析异常
   */
  public <K, V> Map<K, V> toMap() throws IOException {
    final TypeReference<HashMap> type = new TypeReference<HashMap>() {

    };
    return read(objectMapper.readerFor(type));
  }

  /**
   * 将JSON数据解析并存一个List对象中
   *
   * @param elementClass List的元素值类型
   * @return 存储JSON数据的List对象
   * @throws IOException JSON数据解析异常
   */
  public <E> List<E> toList(Class<E> elementClass) throws IOException {
    JavaType type = objectMapper.getTypeFactory()
        .constructCollectionType(ArrayList.class, elementClass);
    return read(objectMapper.readerFor(type));
  }

  /**
   * 将JSON数据解析并存一个List对象中
   *
   * @return 存储JSON数据的List对象
   * @throws IOException JSON数据解析异常
   */
  public <E> List<E> toList() throws IOException {
    final TypeReference<ArrayList> type = new TypeReference<ArrayList>() {

    };
    return read(objectMapper.readerFor(type));
  }

  /**
   * 将JSON数据解析并存一个数组中
   *
   * @param elementClass 数组元素值类型
   * @return 存储JSON数据的数组
   * @throws IOException JSON数据解析异常
   */
  public <E> E[] toArray(Class<E> elementClass) throws IOException {
    final TypeFactory typeFactory = TypeFactory.defaultInstance();
    final ArrayType type = typeFactory.constructArrayType(elementClass);
    return read(objectMapper.readerFor(type));
  }

  /**
   * 将JSON数据解析并存一个数组中
   *
   * @return 存储JSON数据的数组
   * @throws IOException JSON数据解析异常
   */
  public Object[] toArray() throws IOException {
    return toArray(Object.class);
  }

  /**
   * 将JSON数据解析并存一个JsonNode对象中
   *
   * @return 存储JSON数据的JsonNode
   * @throws IOException JSON数据解析异常
   */
  public JsonNode toTree() throws IOException {
    if (object instanceof String) {
      return objectMapper.readTree((String) object);
    }
    if (object instanceof InputStream) {
      return objectMapper.readTree((InputStream) object);
    }
    if (object instanceof File) {
      return objectMapper.readTree((File) object);
    }
    if (object instanceof byte[]) {
      return objectMapper.readTree((byte[]) object);
    }
    if (object instanceof Reader) {
      return objectMapper.readTree((Reader) object);
    }
    if (object instanceof URL) {
      return objectMapper.readTree((URL) object);
    }
    return objectMapper.valueToTree(object);
  }

  /**
   * 将JSON数据解析并存一个JsonNode对象中
   *
   * @return 存储JSON数据的JsonNode，如果JSON数据解析异常，则返回NullNode
   */
  public JsonNode toTreeOrNullNode() {
    try {
      return toTree();
    } catch (IOException e) {
      return NullNode.getInstance();
    }
  }

  private <T> T read(ObjectReader reader) throws IOException {
    if (object instanceof String) {
      return reader.readValue((String) object);
    }
    if (object instanceof InputStream) {
      return reader.readValue((InputStream) object);
    }
    if (object instanceof File) {
      return reader.readValue((File) object);
    }
    if (object instanceof byte[]) {
      return reader.readValue((byte[]) object);
    }
    if (object instanceof Reader) {
      return reader.readValue((Reader) object);
    }
    if (object instanceof URL) {
      return reader.readValue((URL) object);
    }
    return reader.readValue((JsonNode) object);
  }
}
